﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace CloudStorageLight.Core
{
    /// <summary>
    /// Blobアイテム
    /// </summary>
    public class BlobItem
    {

        const string keyAccessTokens = "AccessTokens";
        const string keyOwner = "Owner";
        const string keyContentType = "ContentType";
        const string keyTitle = "Title";
        const string keySummary = "Summary";
        const string KeyRemark = "Remark";
        const string KeyLink = "Link";
        public const string keyUploadDate = "LastUploadedDate";
        const string keyBackupMode = "BackupMode";
        const string keyFilePasscode = "FilePasscode";
        const string keyProtectedMode = "ProtectedMode";
        const string keyRmsTemplateId = "RmsTemplateId";
        const string KeyUsersRole = "UsersRole";
        const string keySearchedMode = "IsSearchMode";
        const string keyMaxHistorySize = "MaxHistorySize";
        public const string keyLatestSnapshot = "LatestSnapshot";
        public const string keyIsDeleted = "IsDeleted";
        public const string keyArchiveLastModified = "ArchiveLastModified";
        public const string keyCaseInsensitive = "CaseInsensitive";
        public const string keyUploadableFileExtension = "UploadableFileExtension";
        public const string keyDecryptableFileOnly = "DecryptableFileOnly";
        public const string keyDownloadFromOriginalUrl = "DownloadFromOriginalUrl";

        //public const string keyLastWriteTime = "LastWriteTime";
        //public const string keyCreationTime = "CreationTime";


        /// <summary>
        /// メタデータ
        /// </summary>
        public Dictionary<string, string> Metadata
        {
            get 
            {
                if (_Metadata == null)
                {
                    _Metadata = new Dictionary<string, string>();
                }
                return _Metadata; 
            }
        }
        Dictionary<string, string> _Metadata = null;

        /// <summary>
        /// アクセストークン
        /// </summary>
        public List<BlobAccessToken> AccessTokens
        {
            get
            {

                if (_AccessTokens == null)
                {
                    _AccessTokens = new List<BlobAccessToken>();
                    if (Metadata.ContainsKey(keyAccessTokens))
                    {
                        var accessTokensString = Metadata[keyAccessTokens];
                        if (!string.IsNullOrEmpty(accessTokensString))
                        {
                            foreach (var item in accessTokensString.Split(','))
                            {
                                _AccessTokens.Add(new BlobAccessToken(System.Web.HttpUtility.UrlDecode(item)));
                            }
                        }
                    }
                }
                return _AccessTokens;                
            }
            internal set
            {
                _AccessTokens = value;
            }
        }
        private List<BlobAccessToken> _AccessTokens;


        /// <summary>
        /// アクセストークンの設定
        /// </summary>
        /// <param name="role"></param>
        /// <param name="accessMode"></param>
        public void SetAccessToken(string role, string accessMode)
        {
            role = role.ToLower();
            var data = AccessTokens.FirstOrDefault(x => string.Equals(x.Role,role, StringComparison.InvariantCultureIgnoreCase));
            if (data != null)
            {
                data.SetMode(accessMode);
            }
            else
            {
                AccessTokens.Add(new BlobAccessToken(role + ":" + accessMode));
            }
        }


        /// <summary>
        /// オーナー
        /// </summary>
        public string Owner
        {
            get 
            {
                if (Metadata.ContainsKey(keyOwner)) return Metadata[keyOwner];
                return null;
            }
            set
            {
                Metadata[keyOwner] = value;
            }
        }



        /// <summary>
        /// コンテンツタイプ
        /// </summary>
        public string ContentType
        {
            get
            {
                if (Metadata.ContainsKey(keyContentType)) return Metadata[keyContentType];
                return null;
            }
            set
            {
                Metadata[keyContentType] = value;
            }
        }

        /// <summary>
        /// リンク（この項目が設定されている場合、外部リソースのリンクファイルとみなす）
        /// </summary>
        public string Link
        {
            get
            {
                if (this.Metadata.ContainsKey(KeyLink) && !string.IsNullOrEmpty(this.Metadata[KeyLink]))
                {
                    return HttpUtility.UrlDecode(this.Metadata[KeyLink]);
                }
                else
                {
                    return null;
                }
            }
            set
            {
                this.Metadata[KeyLink] = HttpUtility.UrlEncode(value);
            }

        }

        /// <summary>
        /// エラー情報
        /// </summary>
        public string Remark
        {
            get
            {
                if (this.Metadata.ContainsKey(KeyRemark) && !string.IsNullOrEmpty(this.Metadata[KeyRemark]))
                {
                    return HttpUtility.UrlDecode(this.Metadata[KeyRemark]);
                }
                else
                {
                    return null;
                }
            }
            set
            {
                this.Metadata[KeyRemark] = HttpUtility.UrlEncode(value);
            }

        }

        /// <summary>
        /// コンテナ
        /// </summary>
        public BlobContainer Container { get; set; }

        /// <summary>
        /// Blobパス
        /// </summary>
        [Key]
        public string Path { get; set; }


        /// <summary>
        /// 名前
        /// </summary>
        public string Name
        {
            get 
            {
                return GetFileName(Path);
            }
        }

        /// <summary>
        /// タイトル
        /// 
        /// Blobの中にはファイル名ではなく独自の名前が設定されている場合がある
        /// メールアイテムなど
        /// </summary>
        public string Title
        {
            get
            {
                if (this.Metadata.ContainsKey(keyTitle) && !string.IsNullOrEmpty(this.Metadata[keyTitle]))
                {
                    return HttpUtility.UrlDecode(this.Metadata[keyTitle]);
                }
                else
                {
                    return Name;
                }
            }
            set
            {
                this.Metadata[keyTitle] = HttpUtility.UrlEncode(value);
            }
        }

        /// <summary>
        /// 検索結果サマリーが設定される。
        /// </summary>
        public virtual string SearchContent { get; set; }

        /// <summary>
        /// 内部パス
        /// </summary>
        internal string RelativePath
        {
            get 
            {
                if (Path == "/") return "/";
                return Path.Substring(1);
            }
        }

        public DateTimeOffset? Snapshot { get; set; }


        /// <summary>
        /// 最後に修正された日付
        /// </summary>
        public DateTimeOffset? LastModified { get; set; }

        /// <summary>
        /// アップロードされた時刻
        /// </summary>
        public DateTimeOffset? UploadDate
        {
            get 
            {
 
                if (Metadata.ContainsKey(keyUploadDate)){
                    DateTimeOffset d;
                    if (DateTimeOffset.TryParseExact(Metadata[keyUploadDate], "O", null, System.Globalization.DateTimeStyles.None, out d))
                    {
                        return d;
                    }
                    if (DateTimeOffset.TryParseExact(Metadata[keyUploadDate], "yyyy/MM/dd HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out d))
                    {
                        return d;
                    }
                }                                        
                return (DateTimeOffset?)null;

            }
            set
            {
                if (value == null)
                {
                    Metadata[keyUploadDate] = null;
                }
                else
                {
                    Metadata[keyUploadDate] = value.Value.ToString("O");

                }
            }
        }


        public DateTimeOffset? LatestSnapshot
        {
            get 
            {
                if (Metadata.ContainsKey(keyLatestSnapshot) && !string.IsNullOrEmpty(this.Metadata[keyLatestSnapshot]))
                {
                    return DateTimeOffset.ParseExact(Metadata[keyLatestSnapshot], "O", null);
                }
                else
                {
                    return (DateTimeOffset?)null;
                }
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyLatestSnapshot] = null;
                }
                else
                {
                    Metadata[keyLatestSnapshot] = value.Value.ToString("O");

                }
            }
        }

        public DateTimeOffset? ArchiveLastModified
        {
            get { return Metadata.ContainsKey(keyArchiveLastModified) ? DateTimeOffset.ParseExact(Metadata[keyArchiveLastModified], "O", null) : (DateTimeOffset?)null; }
            set
            {
                if (value == null)
                {
                    Metadata[keyArchiveLastModified] = null;
                }
                else
                {
                    Metadata[keyArchiveLastModified] = value.Value.ToString("O");

                }
            }
        }

        public DateTimeOffset? LastWriteTime {get; set;}

        public DateTimeOffset? CreationTime { get; set; }

        public bool IsArchibveDirty
        {
            get { return !LatestSnapshot.HasValue || ArchiveLastModified != LastModified; }
        }

        /// <summary>
        /// バックアップ
        /// </summary>
        public bool? BackupMode
        {
            get
            {
                if (Metadata.ContainsKey(keyBackupMode) && Metadata[keyBackupMode] != null) return bool.Parse(Metadata[keyBackupMode]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyBackupMode] = null;
                }
                else
                {
                    Metadata[keyBackupMode] = value.ToString();
                }
            }
        }

        /// <summary>
        /// 履歴サイズ
        /// </summary>
        public int? MaxHistorySize
        {
            get
            {
                if (Metadata.ContainsKey(keyMaxHistorySize) && Metadata[keyMaxHistorySize] != null) return int.Parse(Metadata[keyMaxHistorySize]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyMaxHistorySize] = null;
                }
                else
                {
                    Metadata[keyMaxHistorySize] = value.ToString();
                }
            }
        }

        /// <summary>
        /// 削除済？
        /// </summary>
        public bool IsDeleted
        {
            get
            {
                if (Metadata.ContainsKey(keyIsDeleted) && Metadata[keyIsDeleted] != null) return true;
                return false;
            }
            set
            {
                if (value == false)
                {
                    Metadata[keyIsDeleted] = null;
                }
                else
                {
                    Metadata[keyIsDeleted] = "true";
                }
            }
        }

        /// <summary>
        /// ファイルパスコード
        /// </summary>
        public string FilePasscode
        {
            get
            {
                if (Metadata.ContainsKey(keyFilePasscode)) return Metadata[keyFilePasscode];
                return null;
            }
            set
            {
                Metadata[keyFilePasscode] = value;
            }
        }

        /// <summary>
        /// RMSテンプレートID
        /// </summary>
        public string RmsTemplateId
        {
            get
            {
                if (Metadata.ContainsKey(keyRmsTemplateId)) return Metadata[keyRmsTemplateId];
                return null;
            }
            set
            {
                Metadata[keyRmsTemplateId] = value;
            }
        }

        /// <summary>
        /// 保護モード
        /// </summary>
        /// <remarks>
        /// null:上位フォルダ継承
        /// None:なし
        /// RMS:RMSによる保護
        /// Password:パスワードによる保護
        /// UserPassword:ユーザパスワードによる保護
        /// </remarks>
        public string ProtectedMode
        {
            get
            {
                if (Metadata.ContainsKey(keyProtectedMode)) return Metadata[keyProtectedMode];
                return null;
            }
            set
            {
                Metadata[keyProtectedMode] = value;
            }
        }



        /// <summary>
        /// ディレクトリに保存されているアイテムが検索対象のファイルかどうか
        /// </summary>
        public bool? SearchedMode
        {
            get
            {
                if (Metadata.ContainsKey(keySearchedMode) && Metadata[keySearchedMode] != null) return bool.Parse(Metadata[keySearchedMode]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keySearchedMode] = null;
                }
                else
                {
                    Metadata[keySearchedMode] = value.ToString();
                }
            }
        }


        /// <summary>
        /// Usersグループに含まれるユーザ
        /// </summary>
        public List<string> UsersRoleMembers
        {
            get
            {
                if (_UsersRoleMembers == null)
                {
                    if (this.Metadata.ContainsKey(KeyUsersRole) && !string.IsNullOrEmpty(this.Metadata[KeyUsersRole]))
                    {
                        _UsersRoleMembers = HttpUtility.UrlDecode(this.Metadata[KeyUsersRole]).Split(',').ToList();
                    }
                    else
                    {
                        _UsersRoleMembers = new List<string>();
                    }
                }
                return _UsersRoleMembers;
            }
            set
            {
                _UsersRoleMembers = value;
                this.Metadata[KeyUsersRole] = HttpUtility.UrlEncode(string.Join(",", value));
            }

        }
        private List<string> _UsersRoleMembers;

        public string UsersRole
        {
            get
            {
                return string.Join("\n", UsersRoleMembers);
            }
            set
            {
                if (value != null)
                {
                    UsersRoleMembers = value.Split(',', '\n', '\r').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).ToList();
                }
                else
                {
                    UsersRoleMembers = new List<string>();
                }
            }

        }


        /// <summary>
        /// CaseInsensitive
        /// </summary>
        public bool? CaseInsensitive
        {
            get
            {
                if (Metadata.ContainsKey(keyCaseInsensitive) && Metadata[keyCaseInsensitive] != null) return bool.Parse(Metadata[keyCaseInsensitive]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyCaseInsensitive] = null;
                }
                else
                {
                    Metadata[keyCaseInsensitive] = value.ToString();
                }
            }
        }

        /// <summary>
        /// パスワードで解除可能なファイルのみアップロード可能
        /// </summary>
        public bool? DecryptableFileOnly 
        {
            get
            {
                if (Metadata.ContainsKey(keyDecryptableFileOnly) && Metadata[keyDecryptableFileOnly] != null) return bool.Parse(Metadata[keyDecryptableFileOnly]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyDecryptableFileOnly] = null;
                }
                else
                {
                    Metadata[keyDecryptableFileOnly] = value.ToString();
                }
            }
        }

        public bool? DownloadFromOriginalUrl
        {
            get
            {
                if (Metadata.ContainsKey(keyDownloadFromOriginalUrl) && Metadata[keyDownloadFromOriginalUrl] != null) return bool.Parse(Metadata[keyDownloadFromOriginalUrl]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyDownloadFromOriginalUrl] = null;
                }
                else
                {
                    Metadata[keyDownloadFromOriginalUrl] = value.ToString();
                }
            }
        }

        /// <summary>
        /// アップロード可能な拡張子
        /// </summary>
        public string UploadableFileExtension 
        {
            get
            {
                if (Metadata.ContainsKey(keyUploadableFileExtension)) return HttpUtility.UrlDecode(Metadata[keyUploadableFileExtension]);
                return null;
            }
            set
            {
                if (value == null)
                {
                    Metadata[keyUploadableFileExtension] = value;
                }
                else
                {
                    this.Metadata[keyUploadableFileExtension] = HttpUtility.UrlEncode(value);
                }
            }
        }


        /// <summary>
        /// コンテンツの長さ
        /// </summary>
        public long Length { get; set; }

        /// <summary>
        /// 付帯情報
        /// </summary>
        public object Tag { get; set; }
 
        public BlobItem(BlobContainer container, string path)
        {
            this.Container = container;
            this.Path = path == "//" ? "/" : path;
        }

        /// <summary>
        /// メタ情報の設定
        /// </summary>
        /// <returns></returns>
        public virtual async Task SetMetadataAsync()
        {
            await Container.SetMetadataAsync(this);
        }

        /// <summary>
        /// メタ情報の取得
        /// </summary>
        /// <param name="fetch"></param>
        /// <returns></returns>
        public virtual async Task GetMetadataAsync(bool fetch=true)
        {
            await Container.GetMetadataAsync(this, fetch);
            if (this.IsDeleted)
            {
                this._Metadata=null;
                this.LastModified = null;
            }
        }


        internal virtual void SetAccessTokenMetadataSync()
        {
            if (AccessTokens.Count == 0)
            {
                if (Metadata.ContainsKey(keyAccessTokens)) Metadata[keyAccessTokens] = "";
            }
            else
            {
                Metadata[keyAccessTokens] = string.Join(",", AccessTokens.Select(x => System.Web.HttpUtility.UrlEncode(x.Token)));
            }
        }

        //private CloudBlockBlob CloudBlob
        //{
        //    get
        //    {
        //        if (string.IsNullOrEmpty(RelativePath)) return null;
        //        if (_CloudBlob == null)
        //        {
        //            _CloudBlob = this.Container.fileContainer.GetBlockBlobReference(RelativePath);
        //        }
        //        return _CloudBlob;
        //    }
        //}
        //private CloudBlockBlob _CloudBlob;

        ///// <summary>
        ///// BLOBのURL
        ///// </summary>
        //public Uri Uri { get { return CloudBlob.Uri; } }

        ///// <summary>
        ///// 削除
        ///// </summary>
        ///// <returns></returns>
        //public async Task<bool> DeleteAsync()
        //{
        //    return await Container.DeleteAsync(this);
        //}

        /// <summary>
        /// 公開されたフォルダか
        /// </summary>
        /// <returns></returns>
        public bool IsPublic()
        {
            return AccessTokens.Any(x => x.Role == "guest");
        }

        /// <summary>
        /// 親のパスを取得します
        /// </summary>
        /// <returns></returns>
        public string GetParentPath()
        {
            return GetDirectoryPath(this.Path);
        }

        /// <summary>
        /// ディレクトリパスを取得
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetDirectoryPath(string path)
        {
            var pathwork = path;
            if (pathwork == "/") return null;
            if (pathwork.EndsWith("/")) pathwork = pathwork.Substring(0, pathwork.Length - 1);
            var items = pathwork.Split('/');
            return pathwork.Substring(0, pathwork.Length - items[items.Length - 1].Length);
        }


        /// <summary>
        /// ファイル名前取得
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetFileName(string path)
        {
            var items = path.Split('/');
            var name = string.IsNullOrEmpty(items[items.Length - 1]) ? items[items.Length - 2] : items[items.Length - 1];
            return name;
        }

        public BlobDirectory GetParent()
        {
            var parentPath = GetParentPath();
            if (parentPath == null) return null;
            return new BlobDirectory(Container, parentPath);

        }

        public virtual string GetOriginalUrl()
        {
            if (Container == null) return null;
            return Container.GetOriginalUrl(Path);
        }

        public override string ToString()
        {
            return Path;
        }
    }
}
